package guava;

import com.google.common.base.Charsets;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hashing;
import com.google.common.io.*;
import org.junit.Test;

import java.io.*;
import java.util.List;
import java.util.regex.Pattern;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

/**
 * Files
 * User: Realfighter
 * Date: 2015/4/27
 * Time: 18:32
 */
public class FilesTest {

    @Test
    public void testCopyFile() throws IOException {
        File original = new File("D:\\test.txt");
        File copy = new File("D:\\test2.txt");
        Files.copy(original, copy);
    }

    @Test
    public void testMoveFile() throws IOException {
        File original = new File("D:\\test.txt");
        File newFile = new File("D:\\test2.txt");
        Files.move(original, newFile);
    }

    @Test
    public void readFileIntoListOfStringsTest() throws IOException {
        File file = new File("D:\\test2.txt");
        List<String> expectedLines = Lists.newArrayList("hello world", "this is realfighter", "www.xx566.com");
        List<String> readLines = Files.readLines(file,
                Charsets.UTF_8);
        assertThat(expectedLines, is(readLines));
    }

    @Test
    public void readLinesWithProcessor() throws Exception {
        File file = new File("D:\\test2.txt");
        List<String> expectedLines = Lists.newArrayList("Being A Great Cook", "Art is Fun",
                "Be an Architect", "History of Football", "Gardening My Way");
        List<String> readLines = Files.readLines(file, Charsets.UTF_8,
                new ToListLineProcessor());
        assertThat(expectedLines, is(readLines));
    }

    @Test
    public void testFilesHashing() throws Exception {
        File file = new File("D:\\test2.txt");
        HashCode hashCode = Files.hash(file, Hashing.md5());
        System.out.println(hashCode);
    }

    @Test
    public void appendingWritingToFileTest() throws IOException {
        File file = new File("D:\\test2.txt");
        file.deleteOnExit();
        String hamletQuoteStart = "To be, or not to be";
        Files.write(hamletQuoteStart, file, Charsets.UTF_8);
        assertThat(Files.toString(file, Charsets.UTF_8), is(hamletQuoteStart));
        String hamletQuoteEnd = ",that is the question";
        Files.append(hamletQuoteEnd, file, Charsets.UTF_8);
        assertThat(Files.toString(file, Charsets.UTF_8), is(hamletQuoteStart + hamletQuoteEnd));
        String overwrite = "Overwriting the file";
        Files.write(overwrite, file, Charsets.UTF_8);
        assertThat(Files.toString(file, Charsets.UTF_8), is(overwrite));
    }

    @Test
    public void createByteSourceFromFileTest() throws Exception {
        File f1 = new File("D:\\test2.txt");
        ByteSource byteSource = Files.asByteSource(f1);
        byte[] readBytes = byteSource.read();
        assertThat(readBytes, is(Files.toByteArray(f1)));
    }

    @Test
    public void testCreateFileByteSink() throws Exception {
        File dest = new File("D:\\test.txt");
        dest.deleteOnExit();
        ByteSink byteSink = Files.asByteSink(dest);
        File file = new File("D:\\test2.txt");
        byteSink.write(Files.toByteArray(file));
        assertThat(Files.toByteArray(dest), is(Files.toByteArray(file)));
    }

    @Test
    public void copyToByteSinkTest() throws Exception {
        File dest = new File("D:\\test.txt");
        dest.deleteOnExit();
        File source = new File("D:\\test2.txt");
        ByteSource byteSource = Files.asByteSource(source);
        ByteSink byteSink = Files.asByteSink(dest);
        byteSource.copyTo(byteSink);
        assertThat(Files.toByteArray(dest), is(Files.toByteArray(source)));
    }

    @Test
    public void limitByteStreamTest() throws Exception {
        File binaryFile = new File("D:\\test2.txt");
        BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(binaryFile));
        InputStream limitedInputStream = ByteStreams.limit(inputStream, 10);
        assertThat(limitedInputStream.available(), is(10));
        assertThat(inputStream.available(), is(39));
    }

    @Test
    public void joinTest() throws Exception {
        File f1 = new File("D:\\test.txt");
        File f2 = new File("D:\\test1.txt");
        File f3 = new File("D:\\test2.txt");
        File joinedOutput = new File("D:\\test3.txt");
        joinedOutput.deleteOnExit();
        List<InputSupplier<InputStreamReader>> inputSuppliers = getInputSuppliers(f1, f2, f3);
        InputSupplier<Reader> joinedSupplier = CharStreams.join(inputSuppliers);
        OutputSupplier<OutputStreamWriter> outputSupplier =
                Files.newWriterSupplier(joinedOutput, Charsets.UTF_8);
        String expectedOutputString = joinFiles(f1, f2, f3);
        CharStreams.copy(joinedSupplier, outputSupplier);
        String joinedOutputString = joinFiles(joinedOutput);
        assertThat(joinedOutputString, is(expectedOutputString));
    }

    private String joinFiles(File... files) throws IOException {
        StringBuilder builder = new StringBuilder();
        for (File file : files) {
            builder.append(Files.toString(file, Charsets.UTF_8));
        }
        return builder.toString();
    }

    private List<InputSupplier<InputStreamReader>> getInputSuppliers(File... files) {
        List<InputSupplier<InputStreamReader>> list =
                Lists.newArrayList();
        for (File file : files) {
            list.add(Files.newReaderSupplier(file, Charsets.UTF_8));
        }
        return list;
    }

    @Test
    public void testCloser() throws IOException {
        Closer closer = Closer.create();
        try {
            File destination = new File("D:\\test.txt");
            destination.deleteOnExit();
            BufferedReader reader = new BufferedReader(new FileReader("D:\\test2.txt"));
            BufferedWriter writer = new BufferedWriter(new FileWriter(destination));
            closer.register(reader);
            closer.register(writer);
            String line;
            while ((line = reader.readLine()) != null) {
                writer.write(line);
            }
        } catch (Throwable t) {
            throw closer.rethrow(t);
        } finally {
            closer.close();
        }
    }

    @Test
    public void encodeDecodeTest() throws Exception {
        File file = new File("D:\\test2.txt");
        byte[] bytes = Files.toByteArray(file);
        BaseEncoding baseEncoding = BaseEncoding.base64();
        String encoded = baseEncoding.encode(bytes);
        assertThat(Pattern.matches("[A-Za-z0-9+/=]+", encoded), is(true));
        assertThat(baseEncoding.decode(encoded), is(bytes));
    }

    @Test
    public void encodeByteSinkTest() throws Exception {
        File file = new File("D:\\test2.txt");
        File encodedFile = new File("D:\\test3.txt");
        encodedFile.deleteOnExit();
        CharSink charSink = Files.asCharSink(encodedFile, Charsets.UTF_8);
        BaseEncoding baseEncoding = BaseEncoding.base64();
        ByteSink byteSink = baseEncoding.encodingSink(charSink);
        ByteSource byteSource = Files.asByteSource(file);
        byteSource.copyTo(byteSink);
        String encodedBytes = baseEncoding.encode(byteSource.read());
        assertThat(encodedBytes, is(Files.toString(encodedFile, Charsets.UTF_8)));
    }

}

class ToListLineProcessor implements LineProcessor<List<String>> {
    private static final Splitter splitter = Splitter.on(",");
    private List<String> bookTitles = Lists.newArrayList();
    private static final int TITLE_INDEX = 1;

    @Override
    public boolean processLine(String line) throws IOException {
        bookTitles.add(Iterables.get(splitter.split(line), TITLE_INDEX));
        return true;
    }

    @Override
    public List<String> getResult() {
        return bookTitles;
    }
}